package com.yuntsg.subject_database.component;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import com.alibaba.fastjson.JSONObject;
import com.yuntsg.subject_database.entity.PublicEntity;
import com.yuntsg.subject_database.servic.UpDataCorePaperUnite;
import com.yuntsg.subject_database.util.OftenUtils;
import com.yuntsg.subject_database.util.PublicUtils;
import com.yuntsg.subject_database.util.SmallTool;
import com.yuntsg.subject_database.util.SubDataUtils;
import lombok.Data;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Component
@Data
public class PutNameAndTypeMapComponent {


    Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap = new ConcurrentHashMap<>();
    Map<String, Set<String>> meshAndTermsMap = new ConcurrentHashMap<>();
    Map<Integer, SubDataUtils.JourNameList> jourNameMap = new ConcurrentHashMap<>();
    Map<String, String> md5Map = new ConcurrentHashMap<>();

    // 获取核心期刊关联表的期刊信息，键为期刊唯一JID，值为 中文刊名、英文刊名、ISSN号三部分。
    public static Map<Integer, SubDataUtils.JourNameList> getJourNameMap(String jourTable, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, String> md5Map) throws SQLException {
        Map<Integer, SubDataUtils.JourNameList> jourNameMap = new ConcurrentHashMap<>();
        List<Entity> tableData = Db.use().query("select `id`,`zuhe` from " + jourTable);
        for (Entity entry : tableData) {
            Set<String> issnSet = new HashSet<>();
            Set<String> cnNameSet = new HashSet<>();
            Set<String> enNameSet = new HashSet<>();
            String zuHe = OftenUtils.getStrField(entry, "zuhe");
            Integer jid = OftenUtils.getIntField(entry, "id");
            List<String> list = OftenUtils.strToList(zuHe.toLowerCase());
            for (String str : list) {
                if (str.startsWith("issn") && str.indexOf(" ") > 12 && str.length() > 12 + 2) {
                    String name = str.substring(str.indexOf(" ")).replace("《", "").replace("》", "").toLowerCase().trim();
                    String issn = str.substring(4, str.indexOf(" ")).toLowerCase().trim();
                    issnSet.add(issn);
                    SubDataUtils.putNameAndTypeMap(issn, nameAndTypeMap, "", "sn", md5Map);
                    OftenUtils.addNameSet(name, cnNameSet, enNameSet);
                    SubDataUtils.putNameAndTypeMap(name, nameAndTypeMap, "", "ta", md5Map);
                }
                if (str.startsWith("cn=") && str.indexOf(" ") > 8 && str.length() > 8 + 2) {
                    String name = str.substring(str.indexOf(" ")).replace("《", "").replace("》", "").toLowerCase().trim();
                    String issn = str.substring(3, str.indexOf(" ")).toLowerCase().trim();
                    SubDataUtils.putNameAndTypeMap(issn, nameAndTypeMap, "", "sn", md5Map);
                    issnSet.add(issn);
                    OftenUtils.addNameSet(name, cnNameSet, enNameSet);
                    SubDataUtils.putNameAndTypeMap(name, nameAndTypeMap, "", "ta", md5Map);
                }
                if (str.startsWith("nlmid") && str.length() > 5 + 1) {
                    String issn = str.substring(5).trim();
                    issnSet.add(issn);
                }
            }
            SubDataUtils.JourNameList jourNameList = new SubDataUtils.JourNameList();
            jourNameList.setCnNameList(new ArrayList<>(cnNameSet));
            jourNameList.setEnNameList(new ArrayList<>(enNameSet));
            jourNameList.setIssnList(new ArrayList<>(issnSet));
            jourNameMap.put(jid, jourNameList);
        }
        return jourNameMap;
    }

    //获取标准的单位名称以及标准的部门名称，建成Map，结构为：自由的词，对应标准的词
    public static void getStandardName(String table, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, String> md5Map) throws SQLException {
        String select = "`id`,`info`,`branch`,`hospital`,`standard`,`subordinate`,`exclude`,`type`,`city`";
        List<Entity> tableData = Db.use().query("select " + select + " from " + table);
        for (Entity entry : tableData) {
            Integer id = OftenUtils.getIntField(entry, "id");
            String info = OftenUtils.getStrField(entry, "info");
            String type = OftenUtils.getStrField(entry, "type");
            if (info.length() > 2) {
                String standard = OftenUtils.getStrField(entry, "standard");
                if (type.equals("fund")) {
                    SubDataUtils.putNameAndTypeMap(info, nameAndTypeMap, standard, "fund", md5Map);
                    SubDataUtils.putNameAndTypeMap(standard, nameAndTypeMap, standard, "fund", md5Map);
                }
                if (type.equals("dept") || type.equals("coll")) {
                    SubDataUtils.putNameAndTypeMap(info, nameAndTypeMap, standard, "dept", md5Map);
                    SubDataUtils.putNameAndTypeMap(standard, nameAndTypeMap, standard, "dept", md5Map);
                }
                // 上面的为基金fund、院系coll、部门dept，此三个的top均在字段"standard"中，下面的为大学和医院，如果大学（分校）有值或者医院有值，均保存进去，而必须排除也使用上面的值。
                // 此处暂不考虑总校及分校以及国家等，用分校主要原因是同名同姓大于300时，最多限定在分校，若是总校，范围太大了。此处要求城市必有值，也就是1014版的单位规范不能用了。
                String entryCity = OftenUtils.getStrField(entry, "city");
                if (entryCity.length() > 1) {
                    String branch = OftenUtils.getStrField(entry, "branch");
                    String hospital = OftenUtils.getStrField(entry, "hospital");
//                    System.out.println("id: " + id + " branch： " + branch + " hospital: " + hospital + " info: " + info);
                    if (branch.length() > 3 && branch.contains("-")) {
                        SubDataUtils.putNameAndTypeMap(info, nameAndTypeMap, branch, "unit", md5Map);
                        SubDataUtils.putNameAndTypeMap(branch, nameAndTypeMap, branch, "unit", md5Map);
                    }
                    if (hospital.length() > 3 && hospital.contains("-")) {
                        SubDataUtils.putNameAndTypeMap(info, nameAndTypeMap, hospital, "unit", md5Map);
                        SubDataUtils.putNameAndTypeMap(hospital, nameAndTypeMap, hospital, "unit", md5Map);
                    }
                }
            }
        }
    }

    // 获取meshName对应的各个款目词（包含其自己），以及各个款目词所对应的主题词（也包含其自己），并将标准名称定义为主题词，类型定义为"mh"，这样当用户输入这个词进行检索时，程序会实行扩展检索（即 OR 各个款目词）。
    public static void getMesh(String meshTable, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, Set<String>> meshAndTermsMap, Map<String, String> md5Map) throws SQLException {
        List<Entity> tableData = Db.use().query("select `treeNum`,`entryTerms`,`meshName` from " + meshTable);
        for (Entity entry : tableData) {
            String meshName = OftenUtils.getStrField(entry, "meshName").toLowerCase().trim();
            SubDataUtils.putNameAndTypeMap(meshName, nameAndTypeMap, meshName, "mh", md5Map);
            OftenUtils.putStrSetStr(meshName, meshName, meshAndTermsMap);
            if (meshName.length() > 2) {
                List<String> list = OftenUtils.strToList(OftenUtils.getStrField(entry, "entryTerms").toLowerCase());
                for (String term : list) {
                    if (term.length() > 2) {
                        SubDataUtils.putNameAndTypeMap(term, nameAndTypeMap, meshName, "mh", md5Map);
                        OftenUtils.putStrSetStr(meshName, term.trim(), meshAndTermsMap);
                    }
                }
            }
        }
    }

    @PostConstruct
    public void PutNameAndTypeMap() throws Exception {
        SmallTool.printMess("进行读表corepaper mesh 和单位规则表");
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String jourTable = "scholar.jour_merge_supp_new";
        String meshTable = "scholar.help_mesh_tree_gcr_0615";
        String ruleTable = "scholar.rule_unit_0107";
        String nameAndTypeTable = "scholar.name_and_type_map";

        String limit = "";
//        String limit = " limit 1";
        Integer onePageNum = 1000;

        SmallTool.printMess("开始读取jourTable");
        jourNameMap = getJourNameMap(jourTable, nameAndTypeMap, md5Map);
        System.out.println(format.format(new Date()) + " ① nameAndTypeMap.size()：" + nameAndTypeMap.size());
        SmallTool.printMess("开始读取meshTable");

        getMesh(meshTable, nameAndTypeMap, meshAndTermsMap, md5Map);
        System.out.println(format.format(new Date()) + " ② nameAndTypeMap.size()：" + nameAndTypeMap.size());
        SmallTool.printMess("开始读取ruleTable");

        getStandardName(ruleTable, nameAndTypeMap, md5Map);
        System.out.println(format.format(new Date()) + " ③ nameAndTypeMap.size()：" + nameAndTypeMap.size());
        SmallTool.printMess("开始读取dataTable");
        System.out.println(format.format(new Date()) + " ④ nameAndTypeMap.size()：" + nameAndTypeMap.size());

    }

    // 此部分的功能是ES表入库，分为两部分，一种是需要分词的，此部分拆分为小写的入库和原生的入库，小写的用于检索，原生的用于展示，另一种不作任何改变。
    public void readTable(String dataTable, Integer onePageNum, String limit, String autoId, String field, Map<Integer, SubDataUtils.JourNameList> jourNameMap, Map<String, Set<SubDataUtils.StandardNameAndType>> nameAndTypeMap, Map<String, String> md5Map, boolean print) throws SQLException, IOException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Integer> tempNum = Collections.synchronizedList(new ArrayList<>());
        int from = 1;
        int max = 0;
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + dataTable);
        int count = tableNum.get(0).getInt("count");
        if (count > 0) {
            max = Db.use().queryNumber("select max(" + autoId + ") from " + dataTable).intValue();
        }
        int page = max / onePageNum;
        if (max % onePageNum != 0) {
            page++;
        }
        List<OftenUtils.PageInfo> pageInfos = new ArrayList<>();
        for (int i = 1; i <= page; i++) {
            int start = (i - 1) * onePageNum + from;
            int stop = i * onePageNum + from - 1;
            if (stop > max) {
                stop = max + onePageNum;
            }
            pageInfos.add(new OftenUtils.PageInfo(start, stop));
        }
        int numAll = pageInfos.size();
        pageInfos.parallelStream().forEach(pg -> {
            try {
                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + dataTable + " where " + autoId + " between " + pg.getFrom() + " and " + pg.getTo() + limit);
//                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + dataTable + " where " + autoId + " = 47 ");
                tempNum.add(pg.getFrom());
                if (print && tempNum.size() % 1000 == 0) {
                    System.out.println(format.format(new Date()) + " 现在开始： " + pg.getFrom() + " -- " + pg.getTo() + " 总条数： " + numAll + " 目前已处理： " + tempNum.size() + " 表名： " + dataTable);
                    OftenUtils.printMemory();
                }
                for (Entity entry : tableData) {
                    List<PublicEntity.EnAuthAndCnAuth> en_cn_au = JSONObject.parseArray(OftenUtils.getStrField(entry, "en_cn_au"), PublicEntity.EnAuthAndCnAuth.class);
                    for (PublicEntity.EnAuthAndCnAuth authInfo : en_cn_au) {
                        String enAuth = authInfo.getEnAuth().toLowerCase().trim();
                        if (enAuth.length() > 3) {
                            SubDataUtils.putNameAndTypeMap(enAuth, nameAndTypeMap, "", "enau", md5Map);
                        }
                        String cnAuth = authInfo.getCnAuth();
                        SubDataUtils.putNameAndTypeMap(cnAuth, nameAndTypeMap, "", "cnau", md5Map);
                    }

                    String dataUnique = OftenUtils.getStrField(entry, "data_unique");
                    if (NumberUtil.isInteger(dataUnique)) {
                        int pmid = Integer.parseInt(dataUnique);
                        if (pmid > 0 && pmid < 40000000) {
                            SubDataUtils.putNameAndTypeMap(dataUnique, nameAndTypeMap, "", "pmid", md5Map);
                        }
                    }

                    String doi = OftenUtils.getStrField(entry, "doi").toLowerCase().trim();
                    if (doi.startsWith("10.") && doi.length() > 7) {
                        if (doi.indexOf("; ") > 7) {
                            doi = doi.substring(0, doi.indexOf("; "));
                        }
                        SubDataUtils.putNameAndTypeMap(doi, nameAndTypeMap, "", "doi", md5Map);
                    }

                    Map<String, String> enKwMap = OftenUtils.strToMap(OftenUtils.getStrField(entry, "en_kw_map"));// 英文关键词集合，键为原生的，值为斯坦福后的，在此取斯坦福后的值。
                    for (Map.Entry<String, String> map : enKwMap.entrySet()) {
                        String key = map.getKey();
                        String value = map.getValue();
                        if (OftenUtils.isContainChinese(key)) {
                            SubDataUtils.putNameAndTypeMap(key, nameAndTypeMap, "", "cnkw", md5Map);
                        } else {
                            SubDataUtils.putNameAndTypeMap(key, nameAndTypeMap, "", "enkw", md5Map);
                        }
                        if (OftenUtils.isContainChinese(value)) {
                            SubDataUtils.putNameAndTypeMap(value, nameAndTypeMap, "", "cnkw", md5Map);
                        } else {
                            SubDataUtils.putNameAndTypeMap(value, nameAndTypeMap, "", "enkw", md5Map);
                        }
                    }

                    List<String> cn_kw_list = PublicUtils.strToListToLowerCase(OftenUtils.getStrField(entry, "cn_kw_list"));
                    for (String key : cn_kw_list) {
                        if (OftenUtils.isContainChinese(key)) {
                            SubDataUtils.putNameAndTypeMap(key, nameAndTypeMap, "", "cnkw", md5Map);
                        } else {
                            SubDataUtils.putNameAndTypeMap(key, nameAndTypeMap, "", "enkw", md5Map);
                        }
                    }

                    List<String> meshList = new ArrayList<>();
                    List<String> mhList = new ArrayList<>();
                    List<String> shList = new ArrayList<>();
                    UpDataCorePaperUnite.meshChangeList(OftenUtils.getStrField(entry, "w_mesh").toLowerCase(), meshList, mhList, shList);// [检索]仅NCBI数据库中存在，加权的MeSH，分词，此为组合方式，同时包含主题词和副主题词
                    for (String mesh : meshList) {
                        SubDataUtils.putNameAndTypeMap(mesh, nameAndTypeMap, "", "mesh", md5Map);
                    }

                    SubDataUtils.putNameAndTypeMap(OftenUtils.getStrField(entry, "cn_ti"), nameAndTypeMap, "", "cnti", md5Map);
                    SubDataUtils.putNameAndTypeMap(OftenUtils.getStrField(entry, "en_ti"), nameAndTypeMap, "", "enti", md5Map);


                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        });
    }

}
